package com.boot.portalservice.user.service.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.boot.adminapi.user.domain.dto.RegisterByPasswordReqDTO;
import com.boot.adminapi.user.domain.dto.UserEditReqDTO;
import com.boot.adminapi.user.domain.vo.AppUserVO;
import com.boot.adminapi.user.openfeign.AppUserFeignClient;
import com.boot.commoncore.utils.AESUtil;
import com.boot.commoncore.utils.BeanCopyUtil;
import com.boot.commoncore.utils.VerifyUtil;
import com.boot.commondomain.domain.R;
import com.boot.commondomain.domain.ResultCode;
import com.boot.commondomain.exception.ServiceException;
import com.boot.commonmessage.service.CaptchaService;
import com.boot.commonsecurity.domain.dto.LoginUserDTO;
import com.boot.commonsecurity.domain.dto.TokenDTO;
import com.boot.commonsecurity.service.TokenService;
import com.boot.commonsecurity.utils.JwtUtil;
import com.boot.commonsecurity.utils.SecurityUtil;
import com.boot.portalservice.user.entity.dto.*;
import com.boot.portalservice.user.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 门户用户服务实现类
 */
@Component
@Slf4j
public class UserServiceImpl implements UserService {

    @Autowired
    private AppUserFeignClient appUserFeignClient;

    @Autowired
    private TokenService tokenService;

    @Autowired
    private CaptchaService captchaService;

    /**
     * 登录逻辑
     *
     * @param loginDTO 用户登录DTO
     * @return TokenDTO 加令牌
     */
    @Override
    public TokenDTO login(LoginDTO loginDTO) {
        // 1 需要设置用户声明周期
        LoginUserDTO loginUserDTO = new LoginUserDTO();
        // 2 针对入参进行逻辑分发
        if (loginDTO instanceof WechatLoginDTO wechatLoginDTO) {
            // 3 处理微信登录逻辑
            loginByWechat(wechatLoginDTO, loginUserDTO);
        } else if (loginDTO instanceof CodeLoginDTO codeLoginDTO) {
            // 4 处理验证码登录逻辑
            loginByCode(codeLoginDTO, loginUserDTO);
        } else if (loginDTO instanceof PasswordLoginDTO passwordLoginDTO) {
            // 5 处理手机号密码登录逻辑
            loginByPassword(passwordLoginDTO, loginUserDTO);
        }
        // 6 设置缓存
        loginUserDTO.setUserFrom("app");
        return tokenService.createToken(loginUserDTO);
    }

    /**
     * 发送短信验证码
     *
     * @param phone 手机号
     * @return 验证码
     */
    @Override
    public String sendCode(String phone) {
        if (!VerifyUtil.checkPhone(phone)) {
            throw new ServiceException("手机号格式错误", ResultCode.INVALID_PARA.getCode());
        }
        return captchaService.sendCode(phone);
    }

    /**
     * 修改用户信息
     *
     * @param userEditReqDTO C端用户编辑DTO
     */
    @Override
    public void edit(UserEditReqDTO userEditReqDTO) {
        R<Void> result = appUserFeignClient.edit(userEditReqDTO);
        if (result == null || result.getCode() != ResultCode.SUCCESS.getCode()) {
            throw new ServiceException("修改用户失败");
        }
    }

    /**
     * 获取用户登录信息
     *
     * @return 用户信息DTO
     */
    @Override
    public UserDTO getLoginUser() {
        // 1 获取当前登录的用户
        LoginUserDTO loginUserDTO = tokenService.getLoginUser();
        if (loginUserDTO == null) {
            throw new ServiceException("用户令牌有误", ResultCode.INVALID_PARA.getCode());
        }
        // 2 远程调用获取用户信息
        R<AppUserVO> result = appUserFeignClient.findById(loginUserDTO.getUserId());
        if (result == null || result.getCode() != ResultCode.SUCCESS.getCode() || result.getData() == null) {
            throw new ServiceException("查询用户失败", ResultCode.INVALID_PARA.getCode());
        }
        // 3 对象拼装，返回结果
        UserDTO userDTO = new UserDTO();
        BeanCopyUtil.copyProperties(loginUserDTO, userDTO);
        BeanCopyUtil.copyProperties(result.getData(), userDTO);
        return userDTO;
    }

    /**
     * 退出登录
     */
    @Override
    public void logout() {
        // 1 解析令牌
        String token = SecurityUtil.getToken();
        if (StringUtils.isEmpty(token)) {
            return;
        }
        String userName = JwtUtil.getUserName(token);
        String userId = JwtUtil.getUserId(token);
        log.info("{}退出了系统, 用户ID{}", userName, userId);
        // 2 删除用户缓存记录
        tokenService.delLoginUser(token);
    }
    /**
     * 手机号密码注册
     *
     * @param passwordRegisterReqDTO 用户生命周期信息
     * @return 用户VO
     */
    public void registerByPassword(PasswordRegisterReqDTO passwordRegisterReqDTO) {
        // 1 校验验证码
        String cacheCode = captchaService.getCode(passwordRegisterReqDTO.getPhone());
        if (cacheCode == null) {
            throw new ServiceException("验证码无效", ResultCode.INVALID_PARA.getCode());
        }
        if (!cacheCode.equals(passwordRegisterReqDTO.getCode())) {
            throw new ServiceException("验证码错误", ResultCode.INVALID_PARA.getCode());
        }
        // 2 手机号不为空且符合规则
        if (!StringUtils.isNotBlank(passwordRegisterReqDTO.getPhone()) || !VerifyUtil.checkPhone(passwordRegisterReqDTO.getPhone())){
            throw new ServiceException("手机号为空或格式错误", ResultCode.INVALID_PARA.getCode());
        }
        // 3 密码不为空且符合规则
        // 密码先解密
        String password = AESUtil.decryptHex(passwordRegisterReqDTO.getPassword());
        if (!StringUtils.isNotBlank(passwordRegisterReqDTO.getPassword()) || !VerifyUtil.checkPassword(password)){
            throw new ServiceException("密码为空或格式错误", ResultCode.INVALID_PARA.getCode());
        }
        // 4 处理手机号注册逻辑
        RegisterByPasswordReqDTO registerByPasswordReqDTO = new RegisterByPasswordReqDTO();
        BeanCopyUtil.copyProperties(passwordRegisterReqDTO, registerByPasswordReqDTO);
        R<Void> result = appUserFeignClient.registerByPassword(registerByPasswordReqDTO);
        if (result == null || result.getCode() != ResultCode.SUCCESS.getCode()) {
            log.error("用户注册失败! {}", passwordRegisterReqDTO.getPhone());
        }
        // 5 校验验证码通过就删除Redis中的验证码
        captchaService.deleteCode(passwordRegisterReqDTO.getPhone());
    }
    /**
     * 处理微信登录逻辑
     *
     * @param wechatLoginDTO 微信登录DTO
     * @param loginUserDTO   用户生命周期对象
     */
    private void loginByWechat(WechatLoginDTO wechatLoginDTO, LoginUserDTO loginUserDTO) {
        AppUserVO appUserVO;
        // 1 根据openId进行查询
        R<AppUserVO> result = appUserFeignClient.findByOpenId(wechatLoginDTO.getOpenId());
        // 2 对查询结果进行判断
        if (result == null || result.getCode() != ResultCode.SUCCESS.getCode() || result.getData() == null) {
            // 3 没查到，需要进行注册
            appUserVO = register(wechatLoginDTO);
        } else {
            appUserVO = result.getData();
        }
        // 4 设置登录信息
        loginUserDTO.setUserId(appUserVO.getUserId());
        loginUserDTO.setUserName(appUserVO.getNickName());
    }

    /**
     * 验证码登录处理逻辑
     *
     * @param codeLoginDTO 验证码登录DTO
     * @param loginUserDTO 用户信息上下文DTO
     */
    private void loginByCode(CodeLoginDTO codeLoginDTO, LoginUserDTO loginUserDTO) {
        // 1 校验手机号
        if (!VerifyUtil.checkPhone(codeLoginDTO.getPhone())) {
            throw new ServiceException("手机号格式错误", ResultCode.INVALID_PARA.getCode());
        }
        // 2 执行远程调用
        AppUserVO appUserVO;

        R<AppUserVO> result = appUserFeignClient.findByPhone(codeLoginDTO.getPhone());
        // 3 查不到人的处理逻辑
        if (result == null || result.getCode() != ResultCode.SUCCESS.getCode() || result.getData() == null) {
            appUserVO = register(codeLoginDTO);
        } else {
            appUserVO = result.getData();
        }
        // 4 校验验证码
        String cacheCode = captchaService.getCode(codeLoginDTO.getPhone());
        if (cacheCode == null) {
            throw new ServiceException("验证码无效", ResultCode.INVALID_PARA.getCode());
        }
        if (!cacheCode.equals(codeLoginDTO.getCode())) {
            throw new ServiceException("验证码错误", ResultCode.INVALID_PARA.getCode());
        }
        // 5 校验验证码通过就删除Redis中的验证码
        captchaService.deleteCode(codeLoginDTO.getPhone());
        // 6 设置登录信息
        loginUserDTO.setUserId(appUserVO.getUserId());
        loginUserDTO.setUserName(appUserVO.getNickName());
    }

    /**
     * @param passwordLoginDTO 手机号密码登录DTO
     * @param loginUserDTO     用户信息上下文DTO
     */
    private void loginByPassword(PasswordLoginDTO passwordLoginDTO, LoginUserDTO loginUserDTO) {
        // 1 校验手机号
        if (!VerifyUtil.checkPhone(passwordLoginDTO.getPhone())) {
            throw new ServiceException("手机号格式错误", ResultCode.INVALID_PARA.getCode());
        }
        // 2 执行远程调用
        AppUserVO appUserVO;

        R<AppUserVO> result = appUserFeignClient.findByPhone(passwordLoginDTO.getPhone());
        // 3 查不到人的处理逻辑
        if (result == null || result.getCode() != ResultCode.SUCCESS.getCode() || result.getData() == null) {
            throw new ServiceException("用户还未注册", ResultCode.INVALID_PARA.getCode());
        } else {
            appUserVO = result.getData();
        }
        // 4 校验密码
        // 前端传来的先解密再加密
        String password = AESUtil.decryptHex(passwordLoginDTO.getPassword());
        password = DigestUtil.sha256Hex(password);
        if (!password.equals(appUserVO.getPassword())) {
            throw new ServiceException("密码错误", ResultCode.INVALID_PARA.getCode());
        }
        // 6 手机号密码都正确,设置登录信息
        loginUserDTO.setUserId(appUserVO.getUserId());
        loginUserDTO.setUserName(appUserVO.getNickName());
    }

    /**
     * 根据入参来注册
     *
     * @param loginDTO 用户生命周期信息
     * @return 用户VO
     */
    private AppUserVO register(LoginDTO loginDTO) {

        R<AppUserVO> result = null;

        // 1 针对入参进行逻辑分发
        if (loginDTO instanceof WechatLoginDTO wechatLoginDTO) {
            // 2 处理微信注册逻辑
            result = appUserFeignClient.registerByOpenId(wechatLoginDTO.getOpenId());
            if (result == null || result.getCode() != ResultCode.SUCCESS.getCode() || result.getData() == null) {
                log.error("用户注册失败! {}", wechatLoginDTO.getOpenId());
            }
        } else if (loginDTO instanceof CodeLoginDTO codeLoginDTO) {
            // 3 处理手机号注册逻辑
            result = appUserFeignClient.registerByPhone(codeLoginDTO.getPhone());
            if (result == null || result.getCode() != ResultCode.SUCCESS.getCode() || result.getData() == null) {
                log.error("用户注册失败! {}", codeLoginDTO.getPhone());
            }
        }
        return result == null ? null : result.getData();
    }

}
